Пауль Хоровиц - Искусство схемотехники. Том 2 [Изд.4-е]
Программа 10.5 демонстрирует программирование клавиатуры с использованием прерываний. Вот общая схема: главная процедура выполняет необходимые предустановки, а затем в цикле прерывает значение флага (программного, не аппаратного), который устанавливает обработчик прерываний, обнаружив код «возврата каретки»; когда главная процедура замечает, что флаг установлен, она переходит к заданным действиям над строкой, а затем происходит возврат к циклу проверки флага. Обработчик прерываний, в который передается управление при каждом прерывании, заносит символ в буфер строки, устанавливает флаг, если символ оказался «возвратом каретки» и возвращает управление.
Давайте более пристально посмотрим на программу. После задания адреса порта и адреса вектора прерываний IRQ2 она выделяет 100 байт под буфер строки (первоначально буфер заполняется нулями). Собственно выполнение программы начинается с занесения адреса буфера в адресный[6] регистр SI, обнуление флага конца строки, и помещения адреса обработчика прерываний (который начинается с ΚΒΙΝΤ) в ячейку 28Н. Для того чтобы разрешить контроллеру прерываний 8259 прерывания 2-го уровня, обнулим бит 2 маски (команды IN, AND, OUT); затем разрешим прерывания ЦП и передадим единицу в KBFLAG, что приводит к разрешению трехстабильного драйвера.
Теперь можно работать. После этого программа выполняет цикл, а прямо под «носом» главной процедуры скрыто обрабатывается прерывание до тех пор, пока не будет обнаружено, что "buflg" загадочным образом установился. Флаг и указатель буфера немедленно устанавливаются в исходное состояние (на тот случай, если придет следующее прерывание), затем строка «проглатывается». Хорошим советом было бы пожелать либо быстрее выполнять программу, либо скопировать строку в другой буфер, поскольку через несколько миллисекунд может произойти другое прерывание (сопровождаемое занесением нового байта в буфер), однако за это время можно выполнить несколько тысяч команд, что более чем достаточно для копирования строки.
Обработчик прерываний является отдельной программой, которая не входит в главную процедуру. Он активизируется с помощью прерывания 2-го уровня через свой адрес, который, в свою очередь, заранее был загружен по адресу 28Н. Обработчик в точности знает, что должно быть сделано, и делает это безропотно: он сохраняет содержимое регистра АХ (поскольку планирует разнести последнее вдребезги), считывает символ из порта данных клавиатуры, заносит этот символ в буфер, инкрементирует указатель, дополнительно отображает символ на экран (в эхо-режиме), устанавливает флажок (если был введен символ возврата каретки), посылает сигнал об окончании прерывания контроллеру 8259, восстанавливает содержимое регистра АХ и возвращает управление.
Если вы еще раз взглянете на приведенный выше перечень действий, выполняемых обработчиком, то можно заметить, что мы не упомянули только один этап, а именно, считывание флажков состояний, которые позволяют выяснить, какие из нескольких действий должны быть выполнены. В данном случае, поскольку существует единственная причина, вызывающая прерывание, а именно, запрос на ввод очередного символа с клавиатуры, чтение флажков не требуется. (Программист должен отчетливо понимать, при каких условиях происходит аппаратное прерывание и что требуется для его обработки.)
Несколько замечаний по поводу этой программы: во-первых, даже хотя мы и использовали прерывания, программа выглядит столь же тупой, что и раньше — она постоянно выполняет цикл, проверяя состояние признака конца строки. Однако при необходимости можно организовать цикл и по-другому, если необходимо выполнять еще какие-то действия. Это и происходит на самом деле в нашей программе, в той ее части, которая начинается с метки LINE и выполняет вывод символа «звездочка»; в течение этого времени прерывания обеспечивают занесение новых символов в буфер, в то время как в нашем предыдущем примере без прерываний эти символы были бы потеряны.
Второе замечание можно сформулировать следующим образом: даже в том случае, когда мы используем прерывания, остается беспокойство, не выполняет ли программа какие-либо операции с предыдущей строкой в то время, когда следующая строка уже полностью введена. Конечно, в среднем, программа просто обязана не отставать от ввода с клавиатуры; тем не менее может возникнуть ситуация, при которой обработчик строки случайно затратит много времени, и вам потребуется временный буфер более чем на одну строку. Одно решение этой проблемы — скопировать информацию во второй буфер или поочередно обращаться к каждому из двух буферов. Изящной альтернативой является организация входного потока в виде очереди, устроенной как кольцевой буфер, в котором два указателя обеспечивают извлечение очередного входного символа, а предыдущий символ удаляется. Обработчик прерываний продвигает вперед входной указатель, а обработчик строки продвигает выходной указатель. Подобный кольцевой буфер как правило имеет емкость 256 байт, что позволяет поддерживать обработку нескольких строк.
Третье замечание относится к обработчику прерываний самому по себе. Обычно чем он короче и проще, тем лучше, например, в нем возможна установка флажков для указания на необходимость выполнения в главной процедуре более сложных операций. Если обработчик прерываний будет долго переводить дух после каждого прерывания, вы рискуете потерять данные, источником которых являются другие устройства, вырабатывающие прерывания, поскольку в то время, когда ЦП обрабатывает прерывание, другие прерывания запрещены. В такой ситуации выход заключается в том, чтобы вновь разрешить обработку прерываний в вашем собственном обработчике командой STI после того, как будут выполнены критические отрезки программы, которые должны выполняться в первую очередь. Затем, если возникло прерывание, ваш обработчик прерываний будет сам прерван. Поскольку флажки и адреса возврата сохраняются в стеке, программа сумеет найти обратную дорогу, сначала в ваш обработчик, а затем в главную программу.
10.11. Прерывания в целомНаш пример с клавиатурой демонстрирует суть прерываний, которые являются внезапными аппаратно вырабатываемыми запросами от периферийных устройств, вызывающими программную передачу управления специализированной программе обработки прерывания (которая обычно выполняет программно-управляемый ввод-вывод), а затем возврат управления в ту часть программы, выполнение которой было прервано.
Другим примером устройств, использующих прерывания, являются часы реального времени, в которых периодически (часто 10 раз в секунду, но в ПЭВМ типа IBM PC 18,2 раза в секунду) вырабатываемое прерывание «подталкивает» подпрограмму определения текущего времени; еще одним примером является параллельный интерфейс печатающего устройства, который вырабатывает прерывания всякий раз, когда готов новый символ. Используя прерывания, такие периферийные устройства позволяют компьютеру одновременно выполнять другие задания; вот почему вы можете работать с текстовым редактором, пока ваша ПЭВМ печатает файл (и при этом еще и отсчитывает текущее время).
IBM PC, однако, не иллюстрирует всех возможностей прерываний. Как мы видели, имеются 6 линий IRQ на магистрали, каждая из которых может быть задействована только одним устройством, использующим прерывания. Линии IRQ пронумерованы в соответствии с приоритетом; в том случае, когда вырабатываются несколько прерываний, первым отрабатывается то, номер которого меньше. Четыре из IRQ-линий предопределены для таких устройств, как последовательный порт (IRQ4), жесткий диск (IRQ5), гибкий диск (IRQ6) и порт печатающего устройства (IRQ7), оставляя неиспользуемыми только IRQ2 и IRQ3 [линии, соответствующие двум другим, используемым в IBM PC уровням IRQ, даже не выведены на магистраль, а используются на системной плате для обеспечения работы таймера с частотой пересчета 18,2 Гц (IRQ0) и клавиатуры (IRQ1)]. В том случае, если вы хотите дополнительно подключить стример или локальную сеть, придется использовать IRQ2 и IRQ3. Более того, прерывания отрабатываются по фронту сигнала, что делает тщетным какие бы то ни было разумные попытки использовать проводное «ИЛИ» для того, чтобы подключить несколько периферийных устройств к одной IRQ-линии.
Общие линии прерываний. Обычный протокол обработки прерываний, применяемый во многих компьютерах, обходит подобные ограничения. Посмотрите на рис. 10.13.
Рис. 10.13. Линии прерываний, совместно используемые несколькими устройствами.
Имеется несколько приоритетных линий IRQ-типа; здесь на входах ЦП (или других узлов, непосредственно с ними связанных) используется отрицательная логика. Для того чтобы сформировать сигнал прерывания, вы должны задать низкий уровень потенциала на одной из IRQ'-линий, используя, как показано, микросхему с открытым коллекторным выходом (или с тремя выходными состояниями). (Отметим хитрость, которая заключается в использовании элемента с трехстабильным выходом в качестве имитатора элемента с открытым коллектором.) Линии IRQ', каждая из которых подключена к нагрузочному резистору, используются несколькими периферийными устройствами совместно, так что к каждой IRQ'-линии можно подключить столько устройств, сколько захочется; в нашем примере два порта совместно используют IRQL Вообще говоря, более «нетерпеливое» устройство следует подключать к IRQ'-линии с более высоким приоритетом.